/******************************************************************************
 *
 *  GTBE_mainTestDRDY.c - Full PLL test Program with hardware interface.  Uses the
 *   Georgia Tech Back-end interface board along with a Tiva C Launchpad
 *
 *  Author: Curtis Mayberry
 *  Georgia Tech IMEMS
 *  rev1 March 2014
 *
 *  Originally written for the MRIG gyroscope project
 *
 *  GT BE Peripherals:
 *   ADC
 *    GPIO PB5
 *    SSI2
 *    M1PWM5
 *   DAC
 *    GPIO PE1 - PE3
 *    SSI0
 *    uDMA Software Channel
 *   Data Storage
 *    Flash Memory (see memory map below)
 *   Processing
 *    FPU
 *   Communications
 *    UART0
 *    uDMA
 *
 *  GT BE Hardware Connections:
 *   ADC: ADS1278
 *    INPUTS
 *     TEST0		-> GPIO PE4: PE4
 *     TEST1		-> GPIO PE4: PE4
 *     CLKDIV		-> GPIO PE5: PE5
 *     ~SYNC		-> GPIO PE0: PE0
 *     CLK			-> M1PWM5  : PF1 (option: hardware jumper)
 *     CLK			-> SSI2_CLK: PB4 (option: hardware jumper)
 *     MODE0		-> GPIO PA6: PA6
 *	   MODE1		-> GPIO PA7: PA7
 *	   FORMAT0		-> GPIO PC4: PC4
 *	   FORMAT1		-> GPIO PC5: PC5
 *	   FORMAT2		-> GPIO PC6: PC6
 *	   SERIAL
 *	   SCLK  		-> SSI2_CLK: PB4
 *	   ~DRDY/ FSYNC -> GPIO PB5: PB5 (CS) (SPI Format: ~DRDY)
 *	   DOUT1        -> SSI2_RX:  PB6 (MISO)
 *	   DOUT2		-> GPIO PB0: PB2
 *	   DOUT3		-> GPIO PB1: PB1
 *	   DOUT4		-> GPIO PB2: PB2
 *	   DOUT5		-> GPIO PB3: PB3
 *	   DOUT6		-> GPIO PB7: PB7
 *	   DOUT7		-> GPIO PD6: PD6
 *	   DOUT8		-> GPIO PD7: PD7
 *    OUTPUTS
 *	   NONE
 *	 DAC
 *    INPUTS
 *     ~LDAC_Forcer -> GPIO PE1
 *     ~LDAC_Quad   -> GPIO PE2
 *     ~CLR         -> GPIO PE3
 *	   SERIAL (SPI)
 *     SCLK         -> SSI0_CLK: PA2
 *     ~SYNC		-> SSI0_FSS: PA3 (CS)
 *     SDO		    -> SSI0_RX:  PA4 (MISO)
 *     SDIN         -> SSI0_TX:  PA5 (MOSI)
 *    OUTPUTS
 *     All 4 outputs of each DAC may be used
 *
 *  This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
 *  Unported License. To view a copy of this license, visit
 *  http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
 *  Commons, 444 Castro Street, Suite 900, Mountain View, California, 94041, USA.
 *
 ******************************************************************************/

#include <stdint.h>
#include <stdbool.h>
#include <math.h>
#include "inc/hw_memmap.h"
#include "inc/hw_ssi.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"

// Tivaware
#include "driverlib/rom.h"
#include "driverlib/ssi.h"
#include "driverlib/sysctl.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/fpu.h"
#include "driverlib/pwm.h"
#include "driverlib/interrupt.h"
#include "driverlib/uart.h"
#include "driverlib/flash.h"
//#include "driverlib/timer.h"
#include "driverlib/udma.h"

// Launchpad Drivers
//#include "examples/boards/ek-tm4c123gxl/drivers/rgb.c"

// GTBE Lib
#include "dac_ad5754.h"
#include "adc_ads1278.h"
#include "tw_extension.h"

#ifndef M_PI
#define M_PI                    3.14159265358979323846
#endif

/**************
 * Parameters *
 **************/
#define DAC_ADDRESS_FORCER DAC_ADDR_A

// Flash Memory Map
#define FLASH_ADDR_CODE   0x0000 // 0x0000 - 0x2800 10KB Code (Protected - Execute Only)
#define FLASH_ADDR_SIG_I  0x2800 // 0x2800 - 0x4800  8KB gpADC_reading (Data Storage)
#define FLASH_ADDR_MSIG_I 0x4800 // 0x8800 - 0x6800  8KB gp_msig_I (Data Storage)
#define FLASH_ADDR_MSIG_Q 0x6800 // 0x6800 - 0x8800  8KB gp_msig_Q (Data Storage)
#define FLASH_ADDR_ERR    0x8800 // 0x8800 - 0xA800  8KB gp_err (Data Storage)
#define FLASH_ADDR_PHASE  0xA800 // 0xA800 - 0xB000  8KB gp_currPhase (Data Storage)
#define FLASH_ADDR_NCO_I  0xB000 // 0xB000 - 0xB800  8KB gp_nco_I (Data Storage)
#define FLASH_ADDR_NCO_Q  0xB800 // 0xB800 - 0xC000  8KB gp_nco_Q (Data Storage)

#define FLASH_SIZE_CODE   0x2800 // Size of each buffer
#define FLASH_SIZE_DATA   0x2000

#define FLASH_START_DATA  FLASH_ADDR_SIG_I // Start address of data in flash memory
#define FLASH_LENGTH_DATA 0x9800 // Total length of data
								 // = 0xC000 - 0x2800


 // Library error routine
 #ifdef DEBUG
 void
 __error__(char *pcFilename, uint32_t ui32Line)
 {
 	while(1) {
 			//
 			// Hang on runtime error.
 			//
 	}
 }
 #endif

/*********************
 * Hardware Settings *
 *********************/
#define BIN TWOS_COMPLEMENT // DAC data format in bipolar modes

/********************
 * Global Variables *
 ********************/
// The control table used by the uDMA controller.  This table must be aligned to a 1024 byte boundary.
#pragma DATA_ALIGN(uDMAcontrolTable, 1024)
uint8_t uDMAcontrolTable[1024];

bool g_dataProcessing;
bool g_dataReady = false;

#define BUFFER_SIZE 1
// ADC Buffers
uint32_t gADC_dataBufferByte2[1];
uint32_t gADC_dataBufferByte1[1];
uint32_t gADC_dataBufferByte0[1];
uint32_t gADC_reading[1];

// DAC output value
int32_t g_output;
uint8_t DAC_g_bufferPRI[3];
uint8_t DAC_g_bufferALT[8];
uint8_t DAC_g_bufferSel = DAC_BUFFER_SEL_ALT;
// Signal Gain
uint32_t A = 2;
// UART Command Handling
int32_t g_UARTCommand;

// DSP variables
floatFlash g_sig_I;

/******************
 * GPIO Functions *
 ******************/
/**
 * An Interrupt handler which executes each time ~DRDY goes low.  The
 **/
void intHandlerDRDY(void) {
	uint32_t intStatus;
	bool uDMAstatus;
	intStatus = GPIOIntStatus(GPIO_PORTB_BASE, true);
	GPIOIntClear(GPIO_PORTB_BASE, intStatus);
	if(intStatus == GPIO_INT_PIN_5) {
		// Receive Each byte over SSI
		ROM_SSIDataPut(SSI2_BASE,0x00000000); // Byte 2
		ROM_SSIDataPut(SSI2_BASE,0x00000000); // Byte 1
		ROM_SSIDataPut(SSI2_BASE,0x00000000); // Byte 0

		// Transfer each byte to the buffers
		ROM_SSIDataGet(SSI2_BASE,&gADC_dataBufferByte2[0]);
		ROM_SSIDataGet(SSI2_BASE,&gADC_dataBufferByte1[0]);
		ROM_SSIDataGet(SSI2_BASE,&gADC_dataBufferByte0[0]);
		uDMAstatus = uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX);
		ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
		// Update DAC output
		//DACd_updateDataDig(DAC_ADDR_A | DAC_ADDR_NONE_EH, gDAC_outputBuffer[0], 0x00000000);
		if(uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX)) {
			uDMAChannelRequest(UDMA_CHANNEL_SSI0TX);
		}
		g_dataReady = true; // Flag indicating new data is available
	}
}

/******************
 * uDMA Functions *
 ******************/

/******************
 * UART Functions *
 ******************/
/**
 * Initializes the UART
 **/
 void initUART0(void) {
	 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
	 ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);

	 ROM_GPIOPinConfigure(GPIO_PA0_U0RX);
	 ROM_GPIOPinConfigure(GPIO_PA1_U0TX);
	 ROM_GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);

	 ROM_UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200,
	 (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
 }

/**
 * Initializes the UART interrupt
 **/
 void initUart0Interrupt(void) {
	 ROM_UARTIntEnable(UART0_BASE, UART_INT_RX);
 }

/**
 * Receives commands for the device
 **/
 void uart0MonitorISR(void) {
	 g_UARTCommand = UARTCharGetNonBlocking(UART0_BASE);
 }

/******************
 * NVIC Functions *
 ******************/
/**
 * Sets the priority of the interrupts
 *  The ~DRDY sample ISR is given highest priority
 *  The UART RX ISR is given lower priority
 *  Smaller numbers correspond to higher interrupt priorities; priority 0 is the highest
 *  interrupt priority.
 **/
 initIntPriority(void) {
	 // Highest Priority
	 ROM_IntPrioritySet(INT_GPIOB, 0x00);
	 ROM_IntPrioritySet(INT_SSI0, 0x20);
	 ROM_IntPrioritySet(UART_INT_RX, 0xE0);
	 // Lowest Priority
 }

 /********
  * Main *
  ********/
int main(void) {

	/* System Initialization */
	// Set system clock to 80 MHz (400MHz main PLL (divided by 5 - uses DIV400 bit)  [16MHz external xtal drives PLL]
	ROM_SysCtlClockSet(SYSCTL_SYSDIV_2_5 | SYSCTL_USE_PLL | SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
	twe_initFPU();
	twe_initUART0();
	twe_initProcessingIndicator();

	/* DAC initialization */
	DAC_initDACuDMA(DAC_RANGE_PM5V, DAC_PWR_PUA | DAC_PWR_PUB);
	DAC_initSSI0Int();
	DAC_preinituDMAautoSSI0();
	ROM_uDMAControlBaseSet(&uDMAcontrolTable[0]); // Point at the control table to use for channel control structures.
	DAC_inituDMAautoSSI0();

	/* ADC initialization */
	ADC_initADC();
	ADC_initDRDYint();

	// Initialize Flash
	twe_initFlash(FLASH_ADDR_CODE, 0x0,
				  FLASH_START_DATA,FLASH_LENGTH_DATA); // Doesn't set protection but does erase the data
	g_sig_I.address = FLASH_ADDR_SIG_I;

	SysCtlDelay(1000);

	bool uDMAstatusStart;
	bool uDMAstatusEnd;
	int32_t flashStatus;
	uint32_t flashError = 0;
	// Process data between samples
	while(1) {
		// Collect Data
		if(g_dataReady == true) {
			uDMAstatusStart = uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX);
			g_dataReady = false;
			GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_7, 0x80);
			g_dataProcessing = true;
			gADC_reading[0] = ((((gADC_dataBufferByte2[0]<<16)+(gADC_dataBufferByte1[0]<<8)+(gADC_dataBufferByte0[0]))) >> 8);

			g_sig_I.data.fn[0] = (float) gADC_reading[0];
			if(g_sig_I.address < 0x4800) {
				flashStatus = FlashProgram(g_sig_I.data.uintn,g_sig_I.address,4);
				g_sig_I.address += 4;
				if(flashStatus != 0) {
					flashError++;
				}
			}

			g_output = A * gADC_reading[0];

			/* Update Output Buffers */
			if(DAC_g_bufferSel == DAC_BUFFER_SEL_PRI) {
				DAC_g_bufferSel = DAC_BUFFER_SEL_ALT; // flag to indicate that ALT output buffer
													  // should be sent to DAC

				// Set up the transfer parameters for the SW uDMA channel.  This will
				// configure the transfer buffers and the transfer size.
				//while(uDMAChannelModeGet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT) != UDMA_MODE_STOP) {

				//}
				uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
									   UDMA_MODE_AUTO,
									   DAC_g_bufferALT, (void *)(SSI0_BASE + SSI_O_DR),
									   3);
				//ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
				DAC_g_bufferALT[0] = DAC_ADDRESS_FORCER; // Input reg Command
				DAC_g_bufferALT[1] = (unsigned char)(g_output >> 8); 		 // First data byte
				DAC_g_bufferALT[2] = (unsigned char)(g_output);				 // Second data byte
			}
			else if(DAC_g_bufferSel == DAC_BUFFER_SEL_ALT) {
				DAC_g_bufferSel = DAC_BUFFER_SEL_PRI; // flag to indicate that ALT output buffer
													  // should be sent to DAC
				// Set up the transfer parameters for the SW uDMA channel.  This will
				// configure the transfer buffers and the transfer size.
				//while(uDMAChannelModeGet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT) != UDMA_MODE_STOP) {

				//}
				uDMAChannelTransferSet(UDMA_CHANNEL_SSI0TX | UDMA_PRI_SELECT,
									   UDMA_MODE_AUTO,
									   DAC_g_bufferPRI, (void *)(SSI0_BASE + SSI_O_DR),
									   3);
				//ROM_uDMAChannelEnable(UDMA_CHANNEL_SSI0TX);
				DAC_g_bufferPRI[0] = DAC_ADDRESS_FORCER; // Input reg Command
				DAC_g_bufferPRI[1] = (unsigned char)(g_output >> 8); 		 // First data byte
				DAC_g_bufferPRI[2] = (unsigned char)(g_output);				 // Second data byte
			}
			uDMAstatusEnd = uDMAChannelIsEnabled(UDMA_CHANNEL_SSI0TX);
			GPIOPinWrite(GPIO_PORTC_BASE, GPIO_PIN_7, 0x00);
			g_dataProcessing = false;
			//if(g_sig_I.address > 0x4800) {
				//while(1) {} // Program is complete when the flash is full.
			//}
		}
	}
}
